// 'ob' == offset binary
// 'tag' == Write line numbers before each line output
//
//  TODO: 目前的版本中，从bin文件提取“波形”数据、CDS处理、写入fits文件等功能，全部在
//          同一个函数中实现，代码显得臃肿，不便于后期的更新维护。
//	@2021-10-08:PL端不再将“行号”写入PS端DDR中，因此这里也不再需要使用"cfg->HAS_TAG"

#include "ccd290_extract.h"

void extract_ccd290_16ch( char fname[], char ofits[], ccd290_config* cfg ) {

	printf("--> calling %s:%s\n", __FILE__, __func__);
    printf("--> reading binary data from: %s\n", fname);

    FILE *fb = fopen(fname,"rb");

    int bits_shift = 0;

    int num = cfg->LEN*CH_NUM + CH_NUM*bits_shift;	// total number of 'uint32_t'. 目前FPGA端还未调整好地址递增的问题...
    uint32_t *regs = (uint32_t*)malloc(sizeof(uint32_t)*num);
    int num_chk = fread(regs, sizeof(uint32_t), num, fb);

    if( num_chk != num ){
    	printf("(%s:L:%d) Error in reading binary data: %s\n", __FILE__, __LINE__, fname);
    	printf("@ num_chk = %d\n", num_chk);
    	printf("@ num     = %d\n", num);
    	exit(0);
    }

    fclose(fb);

    int32_t *OS1,  *OS2,  *OS3,  *OS4;
    int32_t *OS5,  *OS6,  *OS7,  *OS8;
    int32_t *OS9,  *OS10, *OS11, *OS12;
    int32_t *OS13, *OS14, *OS15, *OS16;
    
    OS1  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS2  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS3  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS4  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);

    OS5  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS6  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS7  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS8  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);

    OS9  = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS10 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS11 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS12 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);

    OS13 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS14 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS15 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);
    OS16 = (int32_t*)malloc(sizeof(int32_t)*cfg->LEN);

    int i, cnt=0;
    int i_start = CH_NUM*bits_shift;

    for(i=i_start; i<num; i+=CH_NUM) {

        uint32_t udata_OS1  = regs[i+0];
        uint32_t udata_OS2  = regs[i+1];
        uint32_t udata_OS3  = regs[i+2];
        uint32_t udata_OS4  = regs[i+3];

        uint32_t udata_OS5  = regs[i+4];
        uint32_t udata_OS6  = regs[i+5];
        uint32_t udata_OS7  = regs[i+6];
        uint32_t udata_OS8  = regs[i+7];

        uint32_t udata_OS9  = regs[i+8];
        uint32_t udata_OS10 = regs[i+9];
        uint32_t udata_OS11 = regs[i+10];
        uint32_t udata_OS12 = regs[i+11];

        uint32_t udata_OS13 = regs[i+12];
        uint32_t udata_OS14 = regs[i+13];
        uint32_t udata_OS15 = regs[i+14];
        uint32_t udata_OS16 = regs[i+15];

        uint32_t OS_OFFSET = 65535;     // 与FPGA中DSP部分设置的值要一致！！！

        OS1[cnt]  = (int)(udata_OS1 - OS_OFFSET);
        OS2[cnt]  = (int)(udata_OS2 - OS_OFFSET);
        OS3[cnt]  = (int)(udata_OS3 - OS_OFFSET);
        OS4[cnt]  = (int)(udata_OS4 - OS_OFFSET);
        
        OS5[cnt]  = (int)(udata_OS5 - OS_OFFSET);
        OS6[cnt]  = (int)(udata_OS6 - OS_OFFSET);
        OS7[cnt]  = (int)(udata_OS7 - OS_OFFSET);
        OS8[cnt]  = (int)(udata_OS8 - OS_OFFSET);

        OS9[cnt]  = (int)(udata_OS9 - OS_OFFSET);
        OS10[cnt] = (int)(udata_OS10 - OS_OFFSET);
        OS11[cnt] = (int)(udata_OS11 - OS_OFFSET);
        OS12[cnt] = (int)(udata_OS12 - OS_OFFSET);

        OS13[cnt] = (int)(udata_OS13 - OS_OFFSET);
        OS14[cnt] = (int)(udata_OS14 - OS_OFFSET);
        OS15[cnt] = (int)(udata_OS15 - OS_OFFSET);
        OS16[cnt] = (int)(udata_OS16 - OS_OFFSET);

        cnt++;
    }

//	CDS
    int pixel_num_total = cfg->N_ROW * cfg->N_COL * CH_NUM;
    printf("pixel_num_total = %d\n", pixel_num_total);
    printf("pixel_num_total/16 = %d\n", pixel_num_total/16);

    float *ccd = (float*)malloc( sizeof(float) * pixel_num_total );
    uint16_t *ccd_uint16 = (uint16_t*)malloc( sizeof(uint16_t) * pixel_num_total );

    
    for( i = 0; i < pixel_num_total; i++ ) {
        ccd[i]          = 0;
        ccd_uint16[i]   = 0;
    }

//  get offset from ccd290_config * cfg
	uint idx_offset = cfg->idx_offset;

    printf("idx_offset = %d\n", cfg->idx_offset);
    printf("cfg->LEN = %d\n", cfg->LEN);

//  开始拼接图像

    int row_cnt=0, col_cnt=0;
    int pix_idx;
	double nsample = cfg->cds_weight;

    // printf("@debug: nsample = %f\n",nsample);
    // exit(0);

    for( pix_idx = idx_offset; pix_idx < cfg->LEN; pix_idx += cfg->SAMP_NUM_PER_REG) {
    
    //  以下idx可以保证拼接完之后就是正常的图像,但是与邵立要求的数据格式存在一定的差异,调整idx的计算方式即可
        int idx_os1  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*0 + col_cnt );
        int idx_os2  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*1 + col_cnt );
        int idx_os3  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*2 + col_cnt );
        int idx_os4  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*3 + col_cnt );
        
        int idx_os5  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*5 - col_cnt - 1 );
        int idx_os6  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*6 - col_cnt - 1 );
        int idx_os7  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*7 - col_cnt - 1 );
        int idx_os8  = ( row_cnt * cfg->N_COL * (CH_NUM/2) ) + ( cfg->N_COL*8 - col_cnt - 1 );
        
        int idx_os9  = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*8 - col_cnt - 1 );
        int idx_os10 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*7 - col_cnt - 1 );
        int idx_os11 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*6 - col_cnt - 1 );
        int idx_os12 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*5 - col_cnt - 1 );

        int idx_os13 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*3 + col_cnt );
        int idx_os14 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*2 + col_cnt );
        int idx_os15 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*1 + col_cnt );
        int idx_os16 = ( cfg->N_ROW*2 - row_cnt - 1 ) * cfg->N_COL * (CH_NUM/2) + ( cfg->N_COL*0 + col_cnt );

        int scale_a = 1;
        int scale_b = 1;

        if( cfg->use_mock_scan == 1 ){
        // 在接模拟负载进行测试时，人为地做出pre-scan和over-scane
            scale_a = (col_cnt >= (cfg->N_COL-50) || col_cnt < 27 || row_cnt >= (cfg->N_ROW-50) ) ? 0 : 1;
            scale_b = (col_cnt >= (cfg->N_COL-50) || col_cnt < 27 || row_cnt >= (cfg->N_ROW-50) ) ? 0 : 1;
        }

        ccd[idx_os1]    = ( OS1[pix_idx]  / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os2]    = ( OS2[pix_idx]  / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os3]    = ( OS3[pix_idx]  / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os4]    = ( OS4[pix_idx]  / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os5]    = ( OS5[pix_idx]  / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os6]    = ( OS6[pix_idx]  / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os7]    = ( OS7[pix_idx]  / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os8]    = ( OS8[pix_idx]  / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os9]    = ( OS9[pix_idx]  / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os10]   = ( OS10[pix_idx] / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os11]   = ( OS11[pix_idx] / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os12]   = ( OS12[pix_idx] / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os13]   = ( OS13[pix_idx] / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os14]   = ( OS14[pix_idx] / nsample ) + cfg->cds_offset * scale_b;
        ccd[idx_os15]   = ( OS15[pix_idx] / nsample ) + cfg->cds_offset * scale_a;
        ccd[idx_os16]   = ( OS16[pix_idx] / nsample ) + cfg->cds_offset * scale_b;
        
//        printf("ccd[idx_os1] = %f, nsample = %f\n", ccd[idx_os1], nsample);
//        exit(0);

        ccd_uint16[idx_os1] = (uint16_t)(ccd[idx_os1]*4);
        ccd_uint16[idx_os2] = (uint16_t)(ccd[idx_os2]*4);
        ccd_uint16[idx_os3] = (uint16_t)(ccd[idx_os3]*4);
        ccd_uint16[idx_os4] = (uint16_t)(ccd[idx_os4]*4);

        ccd_uint16[idx_os5] = (uint16_t)(ccd[idx_os5]*4);
        ccd_uint16[idx_os6] = (uint16_t)(ccd[idx_os6]*4);
        ccd_uint16[idx_os7] = (uint16_t)(ccd[idx_os7]*4);
        ccd_uint16[idx_os8] = (uint16_t)(ccd[idx_os8]*4);

        ccd_uint16[idx_os9] = (uint16_t)(ccd[idx_os9]*4);
        ccd_uint16[idx_os10] = (uint16_t)(ccd[idx_os10]*4);
        ccd_uint16[idx_os11] = (uint16_t)(ccd[idx_os11]*4);
        ccd_uint16[idx_os12] = (uint16_t)(ccd[idx_os12]*4);

        ccd_uint16[idx_os13] = (uint16_t)(ccd[idx_os13]*4);
        ccd_uint16[idx_os14] = (uint16_t)(ccd[idx_os14]*4);
        ccd_uint16[idx_os15] = (uint16_t)(ccd[idx_os15]*4);
        ccd_uint16[idx_os16] = (uint16_t)(ccd[idx_os16]*4);

    	col_cnt++;  // 必须先+1,然后再进行判断

        if( col_cnt >= cfg->N_COL ) {
            col_cnt -= cfg->N_COL;
            row_cnt++;
        }
    }

    // exit(0);

//	===================================================
//  write CDS process data into fits
    fitsfile *fptr;
    int status;
    long fpixel = 1, naxis = 2, nelements, exposure;
    
    long naxes[2] = {(cfg->N_COL + cfg->HAS_TAG)*(CH_NUM/2), cfg->N_ROW * 2}; // image is 300 pixel wide by 200 rows
    
    if( cfg->include_line_num == 0 ){
    	naxes[0] = cfg->N_COL * (CH_NUM/2);
    	naxes[1] = cfg->N_ROW * 2;
    }

    status = 0;
    
	fits_create_file(&fptr, ofits, &status); /* create new file */

    /*Create the primary array image (16-bit short uinteger pixels) */
/*    	fits_create_img(fptr, SHORT_IMG, naxis, naxes, &status);*/
    // fits_create_img(fptr, USHORT_IMG, naxis, naxes, &status);
    
/*    fits_create_img(fptr, ULONG_IMG, naxis, naxes, &status);*/
/*    fits_create_img(fptr, LONG_IMG, naxis, naxes, &status);*/
    // fits_create_img(fptr, FLOAT_IMG, naxis, naxes, &status);

    if( cfg->use_uint16_format == 1 ){
        fits_create_img(fptr, USHORT_IMG, naxis, naxes, &status);
    } else {
        fits_create_img(fptr, FLOAT_IMG, naxis, naxes, &status);
    }


    /*Write a keyword; must pass the ADDRESS of the value */
    exposure = 3.;
    fits_update_key(fptr, TLONG, "EXPOSURE", &exposure, "Total Exposure Time", &status);

    long speed = 636;
    fits_update_key(fptr, TLONG, "RD_SPD", &speed, "READOUT SPEED (KHz)", &status);

    double temp = -96.5;
    fits_update_key(fptr, TDOUBLE, "TEMP", &temp, "CCD operating temperature", &status);

    double v_rst_gate = 12;
    fits_update_key(fptr, TDOUBLE, "RST_GATE", &v_rst_gate, "RESET GATE (V)", &status);

    double v_rst_dr = 16.25;
    fits_update_key(fptr, TDOUBLE, "RST_DR", &v_rst_dr, "RESET DRAIN (V)", &status);

    double v_out_drain = 30.5;
    fits_update_key(fptr, TDOUBLE, "OUT_DRN", &v_out_drain, "OUTPUT DRAIN (V)", &status);
    
    double v_dp_drain = 29;
    fits_update_key(fptr, TDOUBLE, "DP_DRAIN", &v_dp_drain, "DUMP DRAIN (V)", &status);

    double v_op_1v = 0.5;
    fits_update_key(fptr, TDOUBLE, "OP_1V", &v_op_1v, "OP_1V (V)", &status);


    // 写入头文件信息
    


    /* Write the array of integers to the image */
    nelements = naxes[0] * naxes[1];
    
//	==============================================================================
/*	fits_write_img(fptr, TUSHORT, fpixel, nelements, ccd, &status);*/
/*	fits_write_img(fptr, TUINT, fpixel, nelements, ccd, &status);*/
	// fits_write_img(fptr, TFLOAT, fpixel, nelements, ccd, &status);

    if( cfg->use_uint16_format == 1 ){
        fits_write_img(fptr, TUSHORT, fpixel, nelements, ccd_uint16, &status);
    } else {
        fits_write_img(fptr, TFLOAT, fpixel, nelements, ccd, &status);
    }
//	==============================================================================

    fits_close_file(fptr, &status);

    printf("--> data saved into fits: %s\n", ofits);

    fits_report_error(stderr, status);
//	===================================================

    free(OS1);
    free(OS2);
    free(OS3);
    free(OS4);

    free(OS5);
    free(OS6);
    free(OS7);
    free(OS8);

    free(OS9);
    free(OS10);
    free(OS11);
    free(OS12);

    free(OS13);
    free(OS14);
    free(OS15);
    free(OS16);

    free(regs);
    free(ccd);
    free(ccd_uint16);
}


